/**
 * edurender
 * D3D10Quake3Model.cpp
 * (c) 2009 J. Brad Byrd
 */

#include "D3D10Quake3Model.h"
#include "RenderState.h"

#include "DXUT.h"

er::D3D10Quake3Model *er::D3D10Quake3Model::Create(
	er::Quake3ModelReader *reader, ID3D10Device *device)
{
	D3D10Quake3Model *model = new D3D10Quake3Model(device);
	
	if (!model->Initialize(reader))
	{
		delete model;
		model = 0;
	}
	
	return model;
}

void er::D3D10Quake3Model::Destroy(er::D3D10Quake3Model *model)
{
	delete model;
}

void er::D3D10Quake3Model::Draw(const RenderState &renderState)
{
	D3DXMATRIX wvp = renderState.GetWorldMatrix() * renderState.GetViewMatrix() * renderState.GetProjectionMatrix();
	m_effect->GetVariableByName("g_mvp")->AsMatrix()->SetMatrix(wvp);
	
	UINT size = sizeof(Vertex);
	UINT offset = 0;
	m_device->IASetInputLayout(m_layout);
	m_device->IASetVertexBuffers(0, 1, &m_vb, &size, &offset);
	//m_device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_POINTLIST);
	m_device->IASetPrimitiveTopology(D3D10_PRIMITIVE_TOPOLOGY_TRIANGLELIST);
	m_device->IASetIndexBuffer(m_ib, DXGI_FORMAT_R16_UINT, 0);
	
	m_effect->GetTechniqueByIndex(0)->GetPassByIndex(0)->Apply(0);
	//m_device->Draw(m_vertexCount, 0);
	m_device->DrawIndexed(m_indexCount, 0, 0);
}

bool er::D3D10Quake3Model::Initialize(er::Quake3ModelReader *reader)
{
	std::vector<Vertex> vertices;
	std::vector<unsigned __int16> indices;
		
	for (unsigned __int32 i = 0; i < reader->GetSurfaceCount(); ++i)
	{
		const Quake3ModelReader::Surface *surface = reader->GetSurface(i);
		size_t offset = vertices.size();
	
		const Quake3ModelReader::XyzNormal *xyzNormals = surface->GetXyzNormals(0);
	
		for (unsigned __int32 j = 0; j < surface->GetXyzNormalsCount(); ++j)
		{
			const Quake3ModelReader::XyzNormal *xyzNormal = &xyzNormals[j];
			
			Vertex vertex;
			vertex.position.x = xyzNormal->x * Quake3ModelReader::MD3_XYZ_SCALE;
			vertex.position.y = xyzNormal->y * Quake3ModelReader::MD3_XYZ_SCALE;
			vertex.position.z = xyzNormal->z * Quake3ModelReader::MD3_XYZ_SCALE;

			float lat = ((xyzNormal->normal >> 8) & 255) * (2.0f * 3.1415926f) / 255.0f;
			float lng = (xyzNormal->normal & 255) * (2.0f * 3.1415926f) / 255.0f;

			vertex.normal.x = cosf(lat) * sinf(lng);
			vertex.normal.y = sinf(lat) * sinf(lng);
			vertex.normal.z = cosf(lng);
			
			::D3DXVec3Normalize(&vertex.normal, &vertex.normal);
			
			vertices.push_back(vertex);
		}
		
		const unsigned __int32 *surfaceIndices = surface->GetIndices();
		
		for (unsigned __int32 j = 0; j < surface->GetIndexCount(); ++j)
		{
			indices.push_back(static_cast<unsigned __int16>(surfaceIndices[j] + offset));
		}
	}
	
	m_vertexCount = vertices.size();
	m_indexCount = indices.size();
	
	D3D10_BUFFER_DESC bd;
	bd.ByteWidth = sizeof(Vertex) * vertices.size();
	bd.Usage = D3D10_USAGE_IMMUTABLE;
	bd.BindFlags = D3D10_BIND_VERTEX_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	
	D3D10_SUBRESOURCE_DATA srd;
	srd.pSysMem = &vertices[0];
	srd.SysMemPitch = 0;
	srd.SysMemSlicePitch = 0;
		
	if (!SUCCEEDED(m_device->CreateBuffer(&bd, &srd, &m_vb)))
		return false;

	bd.ByteWidth = sizeof(unsigned __int16) * indices.size();
	bd.Usage = D3D10_USAGE_IMMUTABLE;
	bd.BindFlags = D3D10_BIND_INDEX_BUFFER;
	bd.CPUAccessFlags = 0;
	bd.MiscFlags = 0;
	
	srd.pSysMem = &indices[0];
	srd.SysMemPitch = 0;
	srd.SysMemSlicePitch = 0;
	
	if (!SUCCEEDED(m_device->CreateBuffer(&bd, &srd, &m_ib)))
		return false;

	ID3D10Blob *blob = 0;
	HRESULT hr = ::D3DX10CreateEffectFromFile(L"Quake3Model.fx", NULL, NULL, "fx_4_0", 0, 0, m_device, NULL, NULL, &m_effect, &blob, NULL);

	if (!SUCCEEDED(hr))
	{
		if (blob != 0)
		{
			MessageBoxA(NULL, static_cast<LPCSTR>(blob->GetBufferPointer()), "Blarg!", 0);
			blob->Release();
		}
		
		return false;
	}

	D3D10_INPUT_ELEMENT_DESC layout[] = 
	{
		{ "POSITION", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT, D3D10_INPUT_PER_VERTEX_DATA, 0 },
		{ "NORMAL", 0, DXGI_FORMAT_R32G32B32_FLOAT, 0, D3D10_APPEND_ALIGNED_ELEMENT, D3D10_INPUT_PER_VERTEX_DATA, 0 },
	};
	
	D3D10_PASS_DESC pd;
	m_effect->GetTechniqueByIndex(0)->GetPassByIndex(0)->GetDesc(&pd);
	hr = m_device->CreateInputLayout(layout, sizeof(layout) / sizeof(layout[0]), pd.pIAInputSignature, pd.IAInputSignatureSize, &m_layout);

	if (!SUCCEEDED(hr))
		return false;

	return true;
}

er::D3D10Quake3Model::D3D10Quake3Model(ID3D10Device *device) :
	m_device(device),
	m_vb(0),
	m_ib(0),
	m_layout(0),
	m_effect(0),
	m_vertexCount(0),
	m_indexCount(0)
{
	m_device->AddRef();
}

er::D3D10Quake3Model::~D3D10Quake3Model()
{
	SAFE_RELEASE(m_device);
	SAFE_RELEASE(m_vb);
	SAFE_RELEASE(m_ib);
	SAFE_RELEASE(m_layout);
	SAFE_RELEASE(m_effect);
}
